/* Copyright 2018 SiFive, Inc */
/* SPDX-License-Identifier: Apache-2.0 */

// changed a lot for use in Crypto Core project


#ifndef METAL__PMP_H
#define METAL__PMP_H

/*!
 * @file metal/pmp.h
 *
 * @brief API for Configuring Physical Memory Protection on RISC-V Cores
 *
 * The Physical Memory Protection (PMP) interface on RISC-V cores
 * is a form of memory protection unit which allows for a finite number
 * of physical memory regions to be configured with certain access
 * permissions.
 *
 * Additional information about the use and configuration rules for PMPs
 * can be found by reading the RISC-V Privileged Architecture Specification.
 */

#include <stddef.h>
#include <string.h>
//#include <metal/machine.h>



/*!
 * @brief Configuration for a PMP region
 */

// priv is a custom extension
class PMPConfig {
public:
	enum AddrMode {
		OFF = 0, TOR = 1, NA4 = 2, NAPOT = 3
	};
	enum Priv {
		USER = 0, SUPERVISOR = 1, HYPERVISOR = 2, MACHINE = 3
	};
	enum LockMode {
		UNLOCKED = false, LOCKED = true
	};
private:
	bool r;
	bool w;
	bool x;
	AddrMode a;
	LockMode l;
	Priv p;
	uint32_t addr;

public:
	PMPConfig() {
		r = w = x = false;
		p = USER;
		a = OFF;
		l = UNLOCKED;
		addr = 0x00000000;
	}
	PMPConfig(uint8_t data, uint32_t addr = 0x00000000) {
		this->r = !!(data & 0x1);
		this->w = !!(data & 0x2);
		this->x = !!(data & 0x4);
		this->a = AddrMode((data >> 3) & 0x3);
		this->p = Priv((data >> 5) & 0x3);
		this->l = (data & 0x8) ? LOCKED : UNLOCKED;
		this->addr = addr;
	}

	// rwxl(USHM)(OT4N)
	PMPConfig(const char* mode, uint32_t addr = 0x00000000) {
		const char* pat[]={"rR","wW","xX","lL","USHM","OT4N"};

		for (size_t i=0;i<strlen(mode);i++) {
			if (!strchr(pat[i], mode[i])) {
				this->addr = 0xffffffff;
				return;
			}
		}

		this->r = *mode++ == 'R';
		this->w = *mode++ == 'W';
		this->x = *mode++ == 'X';
		this->l = (*mode++ == 'L') ? LOCKED : UNLOCKED;

		switch(*mode++) {
		case 'U': this->p = USER; break;
		case 'S': this->p = SUPERVISOR; break;
		case 'H': this->p = HYPERVISOR; break;
		case 'M': this->p = MACHINE; break;
		default:
			;
		}

		switch (*mode++) {
		case 'O': this->a = OFF; break;
		case 'T': this->a = TOR; break;
		case '4': this->a = NA4; break;
		case 'N': this->a = NAPOT; break;
		default:
			;
		}

		this->addr = addr;
	}

	PMPConfig(bool r, bool w, bool x, AddrMode a, LockMode l, Priv p, uint32_t addr = 0x00000000) {
		this->r = r;
		this->w = w;
		this->x = x;
		this->a = a;
		this->l = l;
		this->p = p;
		this->addr = addr;
	}

	uint32_t toInt() {
		uint32_t res = 0;
		res |= (r) ? 0x1 : 0x0;
		res |= (w) ? 0x2 : 0x0;
		res |= (x) ? 0x4 : 0x0;
		res |= uint32_t(a) << 3;
		res |= uint32_t(p) << 5;
		res |= (l == LOCKED) ? 0x80 : 0x00;
		return res & 0xff;
	}

	void lock() { this->l = LOCKED; }
	void setAddressMode(AddrMode a) { this->a = a; }
	void setPriv(Priv p) { this->p = p; }
	void setReadable(bool b) { this->r = b; }
	void setWritable(bool b) { this->w = b; }
	void setExecutable(bool b) { this->x = b; }
	void setAddress(uint32_t addr) { this->addr = addr; }

	bool isLocked() { return l == LOCKED; }
	bool isReadable() { return r; }
	bool isWritable() { return w; }
	bool isExecutable() { return x; }
	AddrMode getAddrMode() { return a; }
	Priv getPriv() { return p; }
	uint32_t getAddress() { return addr; }
};

class PMPAddress : public PMPConfig {
public:
	PMPAddress(uint32_t addr) : PMPConfig()  {
		setAddress(addr);
	}
};

class PMPOff : public PMPAddress {
public:
	PMPOff() : PMPAddress(0) {
	}
};

namespace PMP {
	bool enable();
};




#endif
